/*
 *  homedepotsim.c
 *  
 *
 *  This is the home depot simulation process.
 *
 */
#include "syscall.h"

typedef enum 
{
	FALSE = 0, 
	TRUE = 1
} boolean;

#define MaxNumberOfClerks 1
#define MaxItemsInShoppingList 5

typedef struct
{
	int productId;
	int price;
}Product;

/* Clerk data */
int nextClerkId = 0;
int clerkLineLock;
int clerkStates[MaxNumberOfClerks];
int clerkLineLengths[MaxNumberOfClerks];
int clerkLineCVs[MaxNumberOfClerks];
int clerkLocks[MaxNumberOfClerks];
int clerkCVs[MaxNumberOfClerks];
Product* itemToGiveClerk[MaxNumberOfClerks];
typedef enum
{
	CLERKWORKING = 0,
	CLERKONBREAK = 1
} ClerkStatus;
int lastItemScanned[MaxNumberOfClerks];

/* Customer data */
int nextCustomerId = 0;

/* Stockboy Data */

void CustomerBehavior()
{
	/* local vars for customer */
	int customerId = 0;
	int shortestLineIndex = -1;
	int shortestLineLength = 999;
	Product* shoppingCart[MaxItemsInShoppingList];
	int currentCartSize = 0;
	int shoppingList[MaxItemsInShoppingList];
	int currentShoppingListSize = 0;	
	
	/* populate the customer's shopping list */
	int i;
	SWrite("Customer [%d] filling shopping list with [%d] items.\n", 
		   sizeof("Customer [%d] filling shopping list with [%d] items.\n"),
		   customerId, MaxItemsInShoppingList);
	for(i=0; i < MaxItemsInShoppingList; i++)
	{
		shoppingList[i] = i;
		currentShoppingListSize = currentShoppingListSize+1;
	}
	
	/* find items */
	SWrite("Customer [%d] is now looking for items.\n", 
		   sizeof("Customer [%d] is now looking for items.\n"),
		   customerId, 0);
	for(i=0; i < currentShoppingListSize; i++)
	{
		Product newProduct;
		newProduct.productId = i;
		newProduct.price = 1;
		shoppingCart[i] = &newProduct;
		currentCartSize = currentCartSize + 1;
	}
	
	/* customer gets in line */
	LockAcquire(clerkLineLock);
	SWrite("Customer [%d] is now getting in line.\n", 
		   sizeof("Customer [%d] is now getting in line.\n"),
		   customerId, 0);
	/* find shortest line */
	for(i=0; i < MaxNumberOfClerks; i++)
	{
		/* clerk is on break */
		if(clerkStates[i] == CLERKONBREAK)
		{
			continue;
		}
		/* clerk is not on break, check his line length */
		if(clerkLineLengths[i] < shortestLineLength)
		{
			shortestLineLength = clerkLineLengths[i];
			shortestLineIndex = i;
		}
	}
	/* if all clerks are on break */
	if(shortestLineIndex == -1)
	{
		/* tell the manager */
		Exit(0);
	}
	clerkLineLengths[shortestLineIndex] += 1;
	SWrite("Customer [%d] getting in line [%d].\n", 
		   sizeof("Customer [%d] getting in line [%d].\n"), 
		   customerId, 0);
	/* put the customer to sleep */
	CVWait(clerkLineCVs[shortestLineIndex], clerkLineLock);
	LockAcquire(clerkLineLock);
	LockAcquire(clerkLocks[shortestLineIndex]);
	SWrite("Customer [%d] is leaving line to go to Clerk [%d].\n", 
		   sizeof("Customer [%d] is leaving line to go to Clerk [%d].\n"),
		   customerId, shortestLineIndex);
	clerkLineLengths[shortestLineIndex]--;
	LockRelease(clerkLineLock);
	CVSignal(clerkCVs[shortestLineIndex], clerkLocks[shortestLineIndex]);
	/* give the first item to the clerk */
	for(i=0;i< currentCartSize;i++)
	{
		LockAcquire(clerkLocks[shortestLineIndex]);
		/* put item into the global var space - let clerk know if the item is the last one */
		itemToGiveClerk[shortestLineIndex] = shoppingCart[i];
		CVSignal(clerkCVs[shortestLineIndex], clerkLocks[shortestLineIndex]);
		LockRelease(clerkLocks[shortestLineIndex]);
		CVWait(clerkCVs[shortestLineIndex], clerkLocks[shortestLineIndex]);
	}
	CVWait(clerkCVs[shortestLineIndex], clerkLocks[shortestLineIndex]);
	LockAcquire(clerkLocks[shortestLineIndex]);
	SWrite("Customer [%d] received receipt from Clerk [%d]\n", 
		  sizeof("Customer [%d] receipt receipt from Clerk [%d]\n"),
		  customerId, shortestLineIndex);
	LockRelease(clerkLocks[shortestLineIndex]);
	SWrite("Customer [%d] is now leaving Home Depot.\n", 
		   sizeof("Customer [%d] is now leaving Home Depot.\n"),
		   customerId, 0);
	Exit(0);
}

void ClerkBehavior()
{
	int i=0;
	/* Local vars for clerk */
	int clerkId = 0;
	int newline;
	
	for(i=0; i < 10; i++)
	{
		Yield();
	}
	
	i = 0;
	while(i < 25000)
	{
		LockAcquire(clerkLineLock);
		/* if the clerk line length is greater than 1 then wake up the customer */
		newline = clerkLineLengths[clerkId];
		if(clerkLineLengths[clerkId] > 0)
		{
			/* signal the customer */
			CVSignal(clerkLineCVs[clerkId], clerkLineLock);
			/* release clerk line lock*/
			LockRelease(clerkLineLock);
			/* wake up a customer */
			SWrite("Clerk [%d] called a customer from line [%d].\n",
				   sizeof("Clerk [%d] called a customer from line [%d].\n"),
				   clerkId, clerkId);
			/* wait on my own clerk lock */
			CVWait(clerkCVs[clerkId], clerkLocks[clerkId]);
			LockAcquire(clerkLocks[clerkId]);
			SWrite("Clerk [%d] is now serving customer.\n",
				   sizeof("Clerk [%d] is now serving customer.\n"),
				   clerkId, 0);
			/* wait for the customer to signal with my lock meaning 
			 * that an item was placed on the counter */
			while(1)
			{
				CVWait(clerkCVs[clerkId], clerkLocks[clerkId]);
				LockAcquire(clerkLocks[clerkId]);
				/* process the item */
				
				/* if last item then do this stuff */
				if(lastItemScanned[clerkId] == 1)
				{
					/* generate receipt and signal customer */
					SWrite("Clerk [%d] giving receipt to customer.\n",
						   sizeof("Clerk [%d] giving receipt to customer.\n"),
						   clerkId, 0);
					CVSignal(clerkCVs[clerkId], clerkLocks[clerkId]);
					LockRelease(clerkLocks[clerkId]);
					break;
				}
				LockRelease(clerkLocks[clerkId]);
			}
			break;
		}
		i++;
		LockRelease(clerkLineLock);
	}
	Exit(0);
}

int main()
{
	int i=0;
	Write("\n\n*** HomeDepot Sim Starting! ***\n\n", 32, ConsoleOutput);
	/* Create all locks */
	clerkLineLock = CreateLock("Clerk Line Lock", 15); 
	for(i=0; i < MaxNumberOfClerks; i++)
	{
		clerkLocks[i] = CreateLock("Clerk Lock", 10);
	}
	
	/* Create all CVs */
	for(i=0; i < MaxNumberOfClerks; i++)
	{
		clerkLineCVs[i] = CreateCV("Clerk Line CV", 13);
		clerkCVs[i] = CreateCV("Clerk CV", 8);
	}
	
	/* Fork all entities */
	Fork(&CustomerBehavior, "Customer 1", 10);
	Fork(&ClerkBehavior, "Clerk 1", 7);
	Exit(0);
}
